其他
一次非常有意思的 SQL 优化经历
点击上方“Java之间”,选择“置顶或者星标”
你关注的就是我关心的!
来源:cnblogs.com/tangyanbo/p/4462734.html
我用的数据库是mysql5.6,下面简单的介绍下场景
课程表:
create table Course(
c_id int PRIMARY KEY,
name varchar(10)
)
数据100条
学生表:
create table Student(
id int PRIMARY KEY,
name varchar(10)
)
数据70000条
学生成绩表SC:
CREATE table SC(
sc_id int PRIMARY KEY,
s_id int,
c_id int,
score int
)
数据70w条
查询目的:
查找语文考100分的考生
查询语句:
select s.* from Student s where s.s_id in
(select s_id from SC sc where sc.c_id = 0 and sc.score = 100 )
执行时间:30248.271s
EXPLAIN
select s.* from Student s where s.s_id in
(select s_id from SC sc where sc.c_id = 0 and sc.score = 100 )
先给sc表的c_id和score建个索引
CREATE index sc_c_id_index on SC(c_id);
CREATE index sc_score_index on SC(score);
再次执行上述查询语句,时间为: 1.054s
查看优化后的sql:
SELECT
`YSB`.`s`.`s_id` AS `s_id`,
`YSB`.`s`.`name` AS `name`
FROM
`YSB`.`Student` `s`
WHERE
< in_optimizer > (
`YSB`.`s`.`s_id` ,< EXISTS > (
SELECT
FROM
`YSB`.`SC` `sc`
WHERE
(
(`YSB`.`sc`.`c_id` = 0)
AND (`YSB`.`sc`.`score` = 100)
AND (
< CACHE > (`YSB`.`s`.`s_id`) = `YSB`.`sc`.`s_id`
)
)
)
)
怎么查看优化后的语句?
select s_id from SC sc where sc.c_id = 0 and sc.score = 100
耗时:0.001s
select s.* from Student s where s.s_id in(7,29,5000)
耗时:0.001s
那么改用连接查询呢?
SELECT s.* from
Student s
INNER JOIN SC sc
on sc.s_id = s.s_id
where sc.c_id=0 and sc.score=100
执行时间是:0.057s
CREATE index sc_s_id_index on SC(s_id);
show index from SC
时间: 1.076s
SELECT
`YSB`.`s`.`s_id` AS `s_id`,
`YSB`.`s`.`name` AS `name`
FROM
`YSB`.`Student` `s`
JOIN `YSB`.`SC` `sc`
WHERE
(
(
`YSB`.`sc`.`s_id` = `YSB`.`s`.`s_id`
)
AND (`YSB`.`sc`.`score` = 100)
AND (`YSB`.`sc`.`c_id` = 0)
)
回到前面的执行计划:
SELECT
s.*
FROM
(
SELECT
*
FROM
SC sc
WHERE
sc.c_id = 0
AND sc.score = 100
) t
INNER JOIN Student s ON t.s_id = s.s_id
执行时间为:0.054s
查看执行计划:
CREATE index sc_c_id_index on SC(c_id);
CREATE index sc_score_index on SC(score);
SELECT
s.*
FROM
(
SELECT
*
FROM
SC sc
WHERE
sc.c_id = 0
AND sc.score = 100
) t
INNER JOIN Student s ON t.s_id = s.s_id
执行时间为:0.001s
那么再来执行下sql:
SELECT s.* from
Student s
INNER JOIN SC sc
on sc.s_id = s.s_id
where sc.c_id=0 and sc.score=100
执行时间0.001s
先回顾下:
show index from SC
SELECT s.* from
Student s
INNER JOIN SC sc
on sc.s_id = s.s_id
where sc.c_id=81 and sc.score=84
执行时间:0.061s
执行计划:
alter table SC drop index sc_c_id_index;
alter table SC drop index sc_score_index;
create index sc_c_id_score_index on SC(c_id,score);
消耗时间为:0.007s
执行计划:
总结:
mysql嵌套子查询效率确实比较低 可以将其优化成连接查询 连接表时,可以先用where条件对表进行过滤,然后做表连接
(虽然mysql会对连表语句做优化)建立合适的索引,必要时建立多列联合索引 学会分析sql执行计划,mysql会对sql进行优化,所以分析执行计划很重要
索引优化
单列索引
select * from user_test_copy where sex = 2 and type = 2 and age = 1
CREATE index user_test_index_sex on user_test_copy(sex);
CREATE index user_test_index_type on user_test_copy(type);
CREATE index user_test_index_age on user_test_copy(age);
查询时间:0.415s
多列索引
create index user_test_index_sex_type_age on user_test(sex,type,age);
select * from user_test where sex = 2 and type = 2 and age = 10
执行时间:0.032s
最左前缀
都会使用到索引,即索引的第一个字段sex要出现在where条件中
select * from user_test where sex = 2
select * from user_test where sex = 2 and type = 2
select * from user_test where sex = 2 and age = 10
索引覆盖
执行时间:0.003s
排序
select * from user_test where sex = 2 and type = 2 ORDER BY user_name
时间:0.139s
create index user_name_index on user_test(user_name)
列类型尽量定义成数值类型,且长度尽可能短,如主键和外键,类型字段等等 建立单列索引 根据需要建立多列联合索引
当单个列过滤之后还有很多数据,那么索引的效率将会比较低,即列的区分度较低,那么如果在多个列上建立索引,那么多个列的区分度就大多了,将会有显著的效率提高。
根据业务场景建立覆盖索引
只查询业务需要的字段,如果这些字段被索引覆盖,将极大的提高查询效率
多表连接的字段上需要建立索引
这样可以极大的提高表连接的效率
where条件字段上需要建立索引 排序字段上需要建立索引 分组字段上需要建立索引 Where条件上不要使用运算函数,以免索引失效